package com.sky.service.impl;

import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.sky.DTO.EmployeeDTO;
import com.sky.constant.MessageConstant;
import com.sky.constant.PasswordConstant;
import com.sky.constant.StatusConstant;
import com.sky.context.ThreadLocalContext;
import com.sky.dto.EmployeeLoginDTO;
import com.sky.dto.EmployeePageQueryDTO;
import com.sky.dto.PasswordEditDTO;
import com.sky.entity.Employee;
import com.sky.exception.AccountLockedException;
import com.sky.exception.AccountNotFoundException;
import com.sky.exception.PasswordErrorException;
import com.sky.mapper.EmployeeMapper;
import com.sky.service.EmployeeService;
import com.sky.vo.pageResultVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;

import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.List;

@Slf4j
@Service
public class EmployeeServiceImpl implements EmployeeService {

    @Autowired
    private EmployeeMapper employeeMapper;

    /**
     * 员工登录
     *
     * @param employeeLoginDTO
     * @return
     */
    public Employee login(EmployeeLoginDTO employeeLoginDTO) {
        String username = employeeLoginDTO.getUsername();
        String password = employeeLoginDTO.getPassword();

        //1、根据用户名查询数据库中的数据
        Employee employee = employeeMapper.getByUsername(username);

        //2、处理各种异常情况（用户名不存在、密码不对、账号被锁定）
        if (employee == null) {
            //账号不存在
            throw new AccountNotFoundException(MessageConstant.ACCOUNT_NOT_FOUND);
        }

        //密码比对
        // TODO 后期需要进行md5加密，然后再进行比对
        //对前端传过来的密码进行md5加密,然后和数据库中的密码进行比对

        password = DigestUtils.md5DigestAsHex(password.getBytes());//md5加密
        //摘要算法工具类DigestUtils
        if (!password.equals(employee.getPassword())) {
            /*和数据库中的密码进行比对,如果不一致,则密码错误,抛出密码错误异常,被全局异常处理器捕获后统一错误信息*/
            //密码错误
            throw new PasswordErrorException(MessageConstant.PASSWORD_ERROR);
        }

        if (employee.getStatus() == StatusConstant.DISABLE) {
            //账号被锁定
            throw new AccountLockedException(MessageConstant.ACCOUNT_LOCKED);
        }

        //3、返回实体对象
        return employee;
    }

    /**
     * 业务:新增员工
     *
     * @param employee
     */
    @Override
    public void save(EmployeeDTO employeeDTO) {
        //把DTO对象转换为实体对象,插入数据库
        Employee employee = new Employee();
        //属性拷贝
        BeanUtils.copyProperties(employeeDTO, employee);
        /*补充前端没有传的实体类字段属性*/
        //1.设置密码:默认值"123456",要进行md5加密
        employee.setPassword(DigestUtils.md5DigestAsHex(PasswordConstant.DEFAULT_PASSWORD.getBytes()));
        //2.设置状态:1表示启,0表示禁用,默认启用
        employee.setStatus(StatusConstant.ENABLE);
//        //3.设置创建和修改的时间
//        employee.setCreateTime(LocalDateTime.now());//为当前时间
//        employee.setUpdateTime(LocalDateTime.now());//为当前时间
//        //4.设置创建用户的id和修改用户的id
//        //todo 从线程局部变量中获取登录用户的id(也就是创建用户和修改用户的id)
//        employee.setCreateUser(ThreadLocalContext.getCurrentLocalUserId());//从线程局部变量中获取登录用户的id
//        employee.setUpdateUser(ThreadLocalContext.getCurrentLocalUserId());//从线程局部变量中获取登录用户的id
//        employee.setCreateUser(10L);//先写死,后期需要从登录用户中获取
//        employee.setUpdateUser(10L);//先写死,后期需要从登录用户中获取
        //使用完threadLocal线程局部变量后,需要手动清理,防止线程安全问题
//        ThreadLocalContext.clear();//清理线程局部变量
        log.info("新增员工成功,员工信息:{}", employee);
        //业务:新增员工
        employeeMapper.saveEmployee(employee);

    }

    /**
     * 分页查询员工
     *
     * @param employeePageQueryDTO
     * @return
     */
    @Override
    public pageResultVO<Employee> page(EmployeePageQueryDTO employeePageQueryDTO) {
        //1. 设置分页参数和排序规则
        PageHelper.startPage(employeePageQueryDTO.getPage(), employeePageQueryDTO.getPageSize(), "update_time desc");
        //2.条件查询员工数据
        Page<Employee> employeePage = employeeMapper.page(employeePageQueryDTO);
        /*
        //以下为另一种写法
        //3.把查询结果转换为pageHelper的Page对象
        Page<Employee> page= (Page<Employee>) employee;
        */
        //4.封装分页结果
        pageResultVO<Employee> pageResultVO = new pageResultVO<>();
        pageResultVO.setTotal(employeePage.getTotal());//封装总记录数
        pageResultVO.setRecords(employeePage.getResult());//封装分页查询结果列表
        //5.返回封装好的分页结果对象VO
        return pageResultVO;
    }

    @Override
    public void updateStatus(Integer status, Long id) {
        //封装到表对应的实体类中,用lombok提供的builder和build方法,构建一个Employee对象
        Employee employee = Employee.builder()
                .id(id)//设置id
                .status(status)//设置状态
                .updateTime(LocalDateTime.now())//设置更新时间
                .build();

        //实现业务:修改员工状态
        employeeMapper.updateStatus(employee);
    }

    /**
     * 业务:修改员工信息
     *
     * @param employeeDTO
     */
    @Override
    public void update(EmployeeDTO employeeDTO) {
        //创建一个Employee对象,把DTO对象中的属性拷贝到Employee对象中
        Employee employee = new Employee();
        //把DTO对象转换为实体对象,插入数据库.从而实现修改员工信息
        BeanUtils.copyProperties(employeeDTO, employee);
        //补充前端没有传的实体类字段属性
        //1.设置修改时间
        employee.setUpdateTime(LocalDateTime.now());
        //2.设置修改用户的当前登录用户的id
        employee.setUpdateUser(ThreadLocalContext.getCurrentLocalUserId());
        //使用完threadLocal线程局部变量后,需要手动清理,防止线程安全问题
        ThreadLocalContext.clear();
        //业务:修改员工信息
        employeeMapper.updateEmployee(employee);
        //todo 后期需要增加修改密码的功能,需要对前端传过来的密码进行md5加密,然后和数据库中的密码进行比对,如果一致,则修改密码,否则不修改密码
    }

    /**
     * 根据id查询员工,可用于查询回显
     *
     * @param id
     * @return
     */
    @Override
    public Employee get(Long id) {

        //业务:根据id查询员工
        Employee employee = employeeMapper.get(id);
        //返回员工对象
        return employee;
    }

    @Override
    public void delete(Long id) {
        //业务:根据id删除员工
        employeeMapper.delete(id);
    }

    /**
     * 根据用户id修改密码
     *
     * @param passwordEditDTO
     */
    @Override
    public void editPassword(PasswordEditDTO passwordEditDTO) {
        //实现业务:修改密码
        //1.取出前端传过来的用户id和新密码已经原始密码
        Long id = passwordEditDTO.getEmpId();//获取用户id
        String newPassword = passwordEditDTO.getNewPassword();//获取新密码
        String oldPassword = passwordEditDTO.getOldPassword();//获取老密码
        //2.根据用户id查询用户信息
        Employee employee = employeeMapper.get(id);
        //2.5.判断用户是否存在
        if (employee == null) {
            throw new AccountNotFoundException(MessageConstant.ACCOUNT_NOT_FOUND);
            //账号不存在
        }
        //3.获取用户的原始密码
        String password = employee.getPassword();
        //4.对前端传过来的老密码进行md5加密,然后和数据库中的密码进行比对
        if (!password.equals(DigestUtils.md5DigestAsHex(oldPassword.getBytes(StandardCharsets.UTF_8)))) {
            //如果不一致,则密码错误,抛出密码错误异常,被全局异常处理器捕获后统一错误信息
            //密码错误
            throw new PasswordErrorException(MessageConstant.PASSWORD_ERROR);
        }
        //5.判断新密码和确认密码是否一致,已经在前端做了校验
        //6.对前端传过来的新密码进行md5加密,然后插入数据库,进行密码修改
        newPassword = DigestUtils.md5DigestAsHex(newPassword.getBytes(StandardCharsets.UTF_8));//md5加密
        //6.5.设置用户实体类中的密码字段属性
        employee.setPassword(newPassword);
        //7.业务实现:更新用户信息,修改密码
        employeeMapper.updatePassword(employee);


//        //3.判断用户是否存在
//        if (employee == null) {
//            //用户不存在
//            throw new AccountNotFoundException(MessageConstant.ACCOUNT_NOT_FOUND);
//        }
//        //4.判断老密码是否正确
//        //对前端传过来的密码进行md5加密,然后和数据库中的密码进行比对
//        oldPassword = DigestUtils.md5DigestAsHex(oldPassword.getBytes(Charset.forName("UTF-8")));//md5加密
//        if (!oldPassword.equals(employee.getPassword())) {
//            //老密码错误
//            throw new PasswordErrorException(MessageConstant.PASSWORD_ERROR);
//        }
//        //5.修改密码
//        //对前端传过来的新密码进行md5加密,然后插入数据库
//        newPassword = DigestUtils.md5DigestAsHex(newPassword.getBytes(Charset.forName("UTF-8")));//md5加密
//        employee.setPassword(newPassword);

    }


}
